package com.wangdong.bitmapcache;

import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.RelativeLayout;

import java.util.HashSet;
import java.util.Set;

/**
 * 下面我通过一个例子来演示一下 LruCache 内存缓存 和 DisLruCache 本地缓存 的使用
 *
 * 主要功能就是一个用 GridView 来展示大量的网络图片, 因为GridView 有服用机制,
 * 	1.滑出屏幕的 ImageView 持有的图片进行回收,再将 ImagView 指定新的图片,从而避免了OOM
 * 	2.同时在 ImageView 加载图片是我们使用了内存 和 硬盘进行双缓存
 * 	3.每一个图片的加载和显示任务都运行在独立的线程（异步任务）中，除非这个图片缓存在 LruCache 中，这种情况下图片会立即显示。
 * 	4.如果需要的图片缓存在 DiskLruCache，也会开启一个独立的线程队列。如果在缓存中都没有正确的图片，任务线程会从网络下载。
 *
 * 	注意:如果是自定义 View 来实现照片墙,没有复用机制的话，那么需要在 ImageView 滑出屏幕后手动回收图片，
 * 	即将ImageView加载一张默认空图片，从而切断与之前图片的引用，让系统可以回收之前图片，避免OOM。
 *
 */
public class MainActivity extends AppCompatActivity implements AbsListView.OnScrollListener {

    private GridView gridview;
    private BitmapCaChe bitmapCache;//LruCache 内存缓存
    private BitmapDiskCache bitmapDiskCache; //本地存储卡缓存

    private Set<BitmapDownloadTask> taskCollection; //图片下载任务

    private int mFirstVisibleItem;
    private int mVisibleItemCount;
    private boolean isFirstEnter = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        bitmapCache = new BitmapCaChe(); //创建 LruCache 内存缓存
        bitmapDiskCache = new BitmapDiskCache(this); //创建 DisLruCache 本地缓存

        taskCollection = new HashSet<BitmapDownloadTask>(); //创建下载任务集合

        gridview = (GridView) findViewById(R.id.gridview); //初始GridView控件
        gridview.setAdapter(new Adapter()); //GridView设置Adapter
        gridview.setOnScrollListener(this); //设置GridView 滑动监听
    }

    /**OnScrollListener 滚动接口回调之调用结束时
     *
     * @param view  正在报告其滚动状态的视图
     * @param firstVisibleItem 第一个可见单元格的索引(忽略如果visibleItemCount=0)
     * @param visibleItemCount  可见item数
     * @param totalItemCount 列表适配器中的总项数。
     */
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        mFirstVisibleItem = firstVisibleItem;
        mVisibleItemCount = visibleItemCount;
        // 下载的任务应该由onScrollStateChanged里调用，但首次进入程序时onScrollStateChanged并不会调用，
        // 因此在这里为首次进入程序开启下载任务。
        if (isFirstEnter && visibleItemCount > 0) {
            loadBitmaps(firstVisibleItem, visibleItemCount);
            isFirstEnter = false;
        }
    }

    /**OnScrollListener 滚动接口回调之触发时候
     *
     * @param view 正在报告其滚动状态的视图。
     * @param scrollState  当前滚动状态。
     * 						SCROLL_STATE_FLING用户之前一直在使用触摸滚动，并进行了一次狂欢。
     *                     SCROLL_STATE_IDLE视图不是滚动的。
     *                     SCROLL_STATE_TOUCH_SCROLL用户正在使用触摸滚动，他们的手指仍然在屏幕上。
     */
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        // 仅当GridView静止时才去下载图片，GridView滑动时取消所有正在下载的任务
        if (scrollState == SCROLL_STATE_IDLE) { //停止滚动
            loadBitmaps(mFirstVisibleItem, mVisibleItemCount); //下载图片
        } else {
            cancelAllTasks();  //开始滚动停止下载图片
        }
    }

    private void loadBitmaps(int firstVisibleItem, int visibleItemCount) {
        try {
            for (int i = firstVisibleItem; i < firstVisibleItem + visibleItemCount; i++) {
                String imageUrl = Images.imageUrls[i];
                BitmapDownloadTask task = new BitmapDownloadTask();
                taskCollection.add(task);
                task.execute(imageUrl);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public int dip2px(float dipValue) {
        final float scale = getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public void showlog(String info) {
        System.out.print("LRU " + info + "\n");
    }

    public void cancelAllTasks() {
        if (taskCollection != null) {
            for (BitmapDownloadTask task : taskCollection) {
                task.cancel(false);
            }
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        bitmapDiskCache.flush();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        bitmapDiskCache.close();
    }




    /**首先继承AsyncTask<开始参数类型,后天执行参数类型,结果返回参数类型*/
    class BitmapDownloadTask extends AsyncTask<String, Void, Bitmap> {

        private String imageUrl;

        /*执行异步任务代码处理*/
        @Override
        protected Bitmap doInBackground(String... params) {
            imageUrl = params[0];//获取图片网址
            Bitmap bitmap = bitmapDiskCache.getBitmapFromDiskCache(imageUrl);//获取
            if (bitmap != null) {
                bitmapCache.addBitmapToMemoryCache(imageUrl, bitmap);
                return bitmap;
            } else {
                bitmapDiskCache.downloadBitmapToDiskCache(imageUrl, new BitmapDiskCache.DownloadListener() {
                    @Override
                    public void downloadSuccess(Bitmap bitmap) {
                        bitmapCache.addBitmapToMemoryCache(imageUrl, bitmap);
                    }
                    @Override
                    public void downloadFail() {
                    }
                });
                return bitmapDiskCache.getBitmapFromDiskCache(imageUrl);
            }
        }
        //ui线程执行
        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView imageView = (ImageView) gridview.findViewWithTag(imageUrl);
            //通过Tag找到对应的ImageView，如果ImageView滑出屏幕，则返回null
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            }
            taskCollection.remove(this);
        }
    }

    /**
     * 适配器 Adapter
     */
    public class Adapter extends BaseAdapter {

        @Override
        public int getCount() {
            return Images.imageUrls.length;
        }

        @Override
        public String getItem(int position) {
            return Images.imageUrls[position];
        }

        @Override
        public long getItemId(int position) {
            return position;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            ViewHolder viewHolder = null;
            if(convertView == null) {
                viewHolder = new ViewHolder();
                convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.image_item, null);
                viewHolder.image = (ImageView) convertView.findViewById(R.id.img);
                convertView.setTag(viewHolder);
            } else {
                viewHolder = (ViewHolder)convertView.getTag();
            }
            //
            RelativeLayout.LayoutParams pra = (RelativeLayout.LayoutParams) viewHolder.image.getLayoutParams();//获取布局参数
            //item中imageview控件布局 高 等于 :(Gridview的宽 减去 2dp*2 )除以3
            pra.height = (gridview.getWidth() - 2*dip2px(2)) / 3;
            //然后再把布局参数设置给 item中ImagView控件
            viewHolder.image.setLayoutParams(pra);//这样就可以让GridView中每个Item适配所有手机屏幕 显示正方形的图片


            //获取数据
            String imageUrl = getItem(position);

            //给每个ImageView设置唯一的tag，防止图片加载错乱

			/* ListView、GridView都有两个方法setTag/findViewWithTag，简单的说就是给Item中的View设置一个唯一的Tag值，
			之后可以通过这个Tag值找到与其对应的View，因为ListView和GridView都有复用机制，所以当Item被滑出屏幕后，有可能会被复用，
			Item中的View也就被设置了另外一个Tag值，所以这时通过之前的Tag寻找View返回的是null。
			这里给ImageView设置了一个唯一Tag值，就是待加载图片的url，之后图片下载完成，再通过这个Tag找到ImageView，
			如果ImageView为null，说明图片已经滑出了屏幕，此时不再加载，否则ImageView加载刚刚下载完成的图片，
			这样防止了下载好的图片加载到别的ImageView而形成错乱。 */

            viewHolder.image.setTag(imageUrl);

            //通过图片HTTP网址作为key值获取内存中缓存bitmap
            Bitmap bitmap = bitmapCache.getBitmapFromMemCache(imageUrl);
            //如果内存缓存中没有为null值
            if (bitmap != null) {
                //如果内存缓存中获取bitmap不为null 就设置显示
                viewHolder.image.setImageBitmap(bitmap);
            } else {
                //否则显示默认系统本地图片
                viewHolder.image.setImageResource(R.drawable.error);
            }
            return convertView;
        }

        public class ViewHolder {
            public ImageView image;
        }

    }
}
